package cn.lix.sc.rpc.server.impl.rpc;

import cn.lix.sc.rpc.impl.common.annotations.RpcService;
import cn.lix.sc.rpc.impl.common.dto.EnhancedRpcRequest;
import cn.lix.sc.rpc.impl.common.exception.BaseException;
import cn.lix.sc.rpc.impl.common.exception.SystemException;
import cn.lix.sc.rpc.impl.common.utils.EnhancedParametersConverter;
import cn.lix.sc.rpc.impl.common.utils.SpringContextUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 增强的RPC服务端Bean处理器，支持多种内容类型和文件流处理
 *
 * @author lix
 * @date 2024
 */
@Configuration
public class EnhancedRpcServerBeanProcessor implements BeanPostProcessor, ImportAware {

    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass());

    private Set<String> locationPatterns = new HashSet<>(8);

    /**
     * key--类全路径名
     * value--class
     */
    static final Map<String, Class<?>> beanCache = new HashMap<>(64);

    /**
     * key--enhanced method key (包含内容类型)
     * value--method
     */
    private static final Map<String, Method> enhancedMethodCache = new HashMap<>(64);

    /**
     * key--类全路径名
     * value--RpcService注解信息
     */
    private static final Map<String, RpcService> serviceMetaCache = new HashMap<>(64);

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);
        if (rpcService == null) {
            return bean;
        }
        cacheEnhancedRemoteInterface(bean.getClass().getInterfaces(), rpcService);
        return bean;
    }

    /**
     * 缓存增强的远程接口信息
     */
    private void cacheEnhancedRemoteInterface(Class<?>[] interfaces, RpcService rpcService) {
        if (interfaces == null || interfaces.length < 1) {
            throw SystemException.error("RpcService注解的类未实现任何远程调用接口");
        }

        for (Class<?> rpcInterface : interfaces) {
            if (locationPatterns.isEmpty() || locationPatterns.stream().noneMatch(e -> rpcInterface.getName().contains(e))) {
                continue;
            }
            cacheEnhancedMethod(rpcInterface, rpcService);
        }
    }

    /**
     * 缓存增强的方法信息
     */
    private void cacheEnhancedMethod(Class<?> remoteInterface, RpcService rpcService) {
        String remoteInterfaceName = remoteInterface.getName();
        beanCache.put(remoteInterfaceName, remoteInterface);
        serviceMetaCache.put(remoteInterfaceName, rpcService);

        for (Method method : remoteInterface.getMethods()) {
            // 为每种支持的内容类型创建方法缓存
            for (RpcService.ContentType contentType : rpcService.contentTypes()) {
                if (EnhancedParametersConverter.supportsContentType(method, contentType)) {
                    String enhancedKey = EnhancedParametersConverter.getEnhancedMethodKey(remoteInterfaceName, method, contentType);
                    enhancedMethodCache.put(enhancedKey, method);
                    log.info("===加载增强远程方法[{}#{}] - 内容类型[{}]成功===",
                            remoteInterfaceName, method.getName(), contentType.name());
                }
            }
        }
    }

    /**
     * 增强的方法调用
     */
    public Object invoke(EnhancedRpcRequest request) {
        Class<?> aClass = beanCache.get(request.getServiceClass());
        if (aClass == null) {
            throw SystemException.error("未找到服务类[{}]", request.getServiceClass());
        }

        // 检查服务是否支持指定的内容类型
        RpcService serviceInfo = serviceMetaCache.get(request.getServiceClass());
        if (serviceInfo != null && !Arrays.asList(serviceInfo.contentTypes()).contains(request.getContentType())) {
            throw SystemException.error("服务[{}]不支持内容类型[{}]", request.getServiceClass(), request.getContentType());
        }

        Object bean;
        try {
            if (StringUtils.isEmpty(request.getBeanName())) {
                bean = SpringContextUtil.getBean(aClass);
            } else {
                bean = SpringContextUtil.getBean(aClass, request.getBeanName());
            }
        } catch (Exception e) {
            throw SystemException.error("获取服务Bean失败: {}", e.getMessage());
        }

        // 根据内容类型生成方法键
        String enhancedMethodKey = request.getMethodKey() + "_" + request.getContentType().name();
        Method method = enhancedMethodCache.get(enhancedMethodKey);

        // 如果没有找到特定内容类型的方法，尝试使用JSON方法
        if (method == null && request.getContentType() != RpcService.ContentType.JSON) {
            enhancedMethodKey = request.getMethodKey() + "_" + RpcService.ContentType.JSON.name();
            method = enhancedMethodCache.get(enhancedMethodKey);
        }

        if (method == null) {
            throw SystemException.error("没有找到对应的远程方法[{}]", request.getMethodKey());
        }

        Object[] objects;
        try {
            objects = EnhancedParametersConverter.convert(request, method.getParameterTypes());
        } catch (Exception e) {
            throw SystemException.error("远程方法[{}]参数转化错误: {}", request.getMethodKey(), e.getMessage());
        }

        try {
            Object result = method.invoke(bean, objects);

            // 处理文件类型返回值
            if (result instanceof InputStream) {
                return convertInputStreamToBytes((InputStream) result);
            } else if (result instanceof MultipartFile) {
                return convertMultipartFileToBytes((MultipartFile) result);
            }

            return result;
        } catch (Exception e) {
            Throwable cause = e.getCause();
            if (cause instanceof BaseException) {
                throw (BaseException) cause;
            }
            log.error("远程方法[{}]调用失败", request.getMethodKey(), e);
            throw SystemException.error("远程[{}]调用失败: {}", request.getMethodKey(), cause != null ? cause.getMessage() : e.getMessage());
        }
    }

    /**
     * 将InputStream转换为byte[]
     */
    private byte[] convertInputStreamToBytes(InputStream inputStream) {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                baos.write(buffer, 0, bytesRead);
            }
            return baos.toByteArray();
        } catch (IOException e) {
            throw SystemException.error("InputStream转换失败: {}", e.getMessage());
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.warn("关闭InputStream失败", e);
            }
        }
    }

    /**
     * 将MultipartFile转换为byte[]
     */
    private byte[] convertMultipartFileToBytes(MultipartFile file) {
        try {
            return file.getBytes();
        } catch (IOException e) {
            throw SystemException.error("MultipartFile转换失败: {}", e.getMessage());
        }
    }

    /**
     * 检查方法是否支持流式传输
     */
    public boolean supportsStreaming(String serviceClass, String methodKey) {
        RpcService serviceInfo = serviceMetaCache.get(serviceClass);
        return serviceInfo != null && serviceInfo.enableStreaming();
    }

    /**
     * 获取方法的返回类型信息
     */
    public Class<?> getMethodReturnType(String serviceClass, String methodKey) {
        String enhancedMethodKey = methodKey + "_" + RpcService.ContentType.JSON.name();
        Method method = enhancedMethodCache.get(enhancedMethodKey);
        return method != null ? method.getReturnType() : null;
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        Map<String, Object> defaultAttrs = importMetadata.getAnnotationAttributes(EnableRpcServer.class.getName(), true);
        if (defaultAttrs != null && !ObjectUtils.isEmpty(defaultAttrs.get("value"))) {
            Collections.addAll(locationPatterns, (String[]) defaultAttrs.get("value"));
        } else {
            this.locationPatterns.add(ClassUtils.getPackageName(importMetadata.getClassName()));
        }
    }

    @Bean(name = "enhancedServerSpringContextUtil")
    public SpringContextUtil enhancedSpringContextUtil() {
        return new SpringContextUtil();
    }
} 